#include <stdio.h>
#include <stdlib.h>
#include "ft6x36_upgrade_firmware.h"
#include "ft6x36.h"

#if USE_FT6X36

#include <stdbool.h>
#include "../../board.h"
#include "../common/i2c.h"
#include "../common/lv_pm.h"
#include "ft6x36.h"
#include "lvgl/src/lv_core/lv_group.h"
#include "lvgl/src/lv_misc/lv_tsrb.h"

#define FTS_DELAY_ERASE_PAGE_2K         60
#define FTS_SIZE_PAGE_2K                4096

#define BYTE_OFF_0(x)           (unsigned char)((x) & 0xFF)
#define BYTE_OFF_8(x)           (unsigned char)(((x) >> 8) & 0xFF)
#define BYTE_OFF_16(x)          (unsigned char)(((x) >> 16) & 0xFF)
#define BYTE_OFF_24(x)          (unsigned char)(((x) >> 24) & 0xFF)
#define FLAGBIT(x)              (0x00000001 << (x))
#define FLAGBITS(x, y)          ((0xFFFFFFFF >> (32 - (y) - 1)) & (0xFFFFFFFF << (x)))

static unsigned short fts_crc16_calc_host(unsigned char *pbuf, unsigned int length)
{
    unsigned short ecc = 0;
    unsigned int i = 0;
    unsigned int j = 0;

    for ( i = 0; i < length; i += 2 ) {
        ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1]));
        for (j = 0; j < 16; j ++) {
            if (ecc & 0x01)
                ecc = (uint16_t)((ecc >> 1) ^ AL2_FCS_COEF);
            else
                ecc >>= 1;
        }
    }

    return ecc;
}

int fts_flash_write_buf(
    unsigned int saddr,
    unsigned char *buf,
    unsigned int len,
    unsigned int delay)
{
    int ret = 0;
    unsigned int i = 0;
    unsigned int j = 0;
    unsigned int packet_number = 0;
    unsigned int packet_len = 0;
    unsigned int addr = 0;
    unsigned int offset = 0;
    unsigned int remainder = 0;
    unsigned int cmdlen = 0;
    unsigned char packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 };
    unsigned char ecc_tmp = 0;
    int ecc_in_host = 0;
    unsigned char val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
    unsigned short read_status = 0;
    unsigned short wr_ok = 0;
    struct fts_upgrade *upg = &fwupgrade;

    printf( "**********write data to flash**********");

    if ((!upg) || (!upg->func || !buf || !len)) {
        printf("upgrade/func/buf/len is invalid");

        return -1;
    }

    printf("data buf start addr=0x%x, len=0x%x", saddr, len);
    packet_number = len / FTS_FLASH_PACKET_LENGTH;
    remainder = len % FTS_FLASH_PACKET_LENGTH;
    if (remainder > 0)
        packet_number++;
    packet_len = FTS_FLASH_PACKET_LENGTH;
    printf("write data, num:%d remainder:%d", packet_number, remainder);

    for (i = 0; i < packet_number; i++) {
        offset = i * FTS_FLASH_PACKET_LENGTH;
        addr = saddr + offset;

        /* last packet */
        if ((i == (packet_number - 1)) && remainder)
            packet_len = remainder;

            packet_buf[0] = FTS_CMD_WRITE;
            packet_buf[1] = BYTE_OFF_16(addr);
            packet_buf[2] = BYTE_OFF_8(addr);
            packet_buf[3] = BYTE_OFF_0(addr);
            packet_buf[4] = BYTE_OFF_8(packet_len);
            packet_buf[5] = BYTE_OFF_0(packet_len);
            cmdlen = 6;

        for (j = 0; j < packet_len; j++) {
            packet_buf[cmdlen + j] = buf[offset + j];
            ecc_tmp ^= packet_buf[cmdlen + j];
        }

        ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, packet_buf, packet_len + cmdlen, 0);

        if (ret < 0) {
            printf("app write fail");

            return ret;
        }
        uos_sleep(delay/5);

        /* read status */
        wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len;
        for (j = 0; j < FTS_RETRIES_WRITE; j++) {
            ret = i2c_read_reg(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, FTS_CMD_FLASH_STATUS, val, 0);
            read_status = (((uint16_t)val[0]) << 8) + val[1];
            /*  FTS_INFO("%x %x", wr_ok, read_status); */
            if (wr_ok == read_status) {
                break;
            }
            uos_sleep(FTS_RETRIES_DELAY_WRITE/5);
        }
    }

    ecc_in_host = (int)ecc_tmp;
    if ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) ||
        (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
        ecc_in_host = (int)fts_crc16_calc_host(buf, len);
    }

    return ecc_in_host;
}

int fts_flash_read_buf(unsigned int saddr, unsigned char *buf, unsigned int len)
{
    int ret = 0;
    unsigned int i = 0;
    unsigned int packet_number = 0;
    unsigned int addr = 0;
    unsigned int offset = 0;
    unsigned int remainder = 0;
    unsigned char wbuf[FTS_CMD_READ_LEN_SPI] = { 0 };
    struct fts_upgrade *upg = &fwupgrade;

    if (!upg || !buf || !len) {
        printf("upgrade/buf is NULL or len is 0");

        return -1;
    }

    packet_number = len / FTS_FLASH_PACKET_LENGTH;
    remainder = len % FTS_FLASH_PACKET_LENGTH;
    if (remainder > 0) {
        packet_number++;
    }
    printf("read packet_number:%d, remainder:%d", packet_number, remainder);


    for (i = 0; i < packet_number; i++) {
        offset = i * FTS_FLASH_PACKET_LENGTH;
        addr = saddr + offset;
        /* last packet */
        if ((i == (packet_number - 1)) && remainder)
            wbuf[0] = FTS_CMD_READ;
            wbuf[1] = BYTE_OFF_16(addr);
            wbuf[2] = BYTE_OFF_8(addr);
            wbuf[3] = BYTE_OFF_0(addr);
            ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, wbuf, FTS_CMD_READ_LEN, 0);

            if (ret < 0) {
                printf("pram/bootloader write 03 command fail");
                return ret;
            }

            uos_sleep(FTS_CMD_READ_DELAY/5); /* must wait, otherwise read wrong data */
            ret = i2c_read_reg(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, FTS_CMD_FLASH_STATUS, buf + offset, 0);

            if (ret < 0) {
                printf("pram/bootloader read 03 command fail");
                return ret;
            }
    }

    return 0;
}

static bool fts_fwupg_check_flash_status(
    unsigned short flash_status,
    int retries,
    int retries_delay)
{
    int i = 0;
    unsigned char val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
    unsigned short read_status = 0;

    for (i = 0; i < retries; i++) {
        i2c_read_regs(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, 0x6A, &val[0],2, 0);
        read_status = (((uint16_t)val[0]) << 8) + val[1];
        if (flash_status == read_status) {

            return true;
        }

        uos_sleep(retries_delay/5);
    }

    printf("flash status fail,ok:%04x,read:%04x,retries:%d\n",
              flash_status, read_status, i);
    return false;
}

int fts_fwupg_erase(unsigned int delay)
{
    int ret = 0;
    bool flag = false;
    unsigned char reg = FTS_CMD_ERASE_APP;

    printf("**********erase now**********\n");

    /*send to erase flash*/
    ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, &reg, 1, 0);
    if (ret < 0) {
        printf("erase cmd fail\n");
        return ret;
    }
    uos_sleep(delay/5);

    /* read status 0xF0AA: success */
    flag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ERASE_OK,
                                        FTS_RETRIES_REASE,
                                        FTS_RETRIES_DELAY_REASE);
    if (!flag) {
        printf("ecc flash status check fail");
        return -1;
    }

    return 0;
}

static int ft6x36_ecc_cal(unsigned int saddr, unsigned int len)
{
    int ret = 0;
    int ecc = 0;
    int i = 0;
    unsigned char wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 };
    unsigned char val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
    unsigned short read_status = 0;

    printf( "**********read out checksum**********");

    /* check sum init */
    wbuf[0] = FTS_CMD_ECC_INIT;
    ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, wbuf, 1, 0);
    if (ret < 0) {
        printf("ecc init cmd write fail");
        return ret;
    }

    wbuf[0] = FTS_CMD_ECC_CAL;
    wbuf[1] = BYTE_OFF_16(saddr);
    wbuf[2] = BYTE_OFF_8(saddr);
    wbuf[3] = BYTE_OFF_0(saddr);
    wbuf[4] = BYTE_OFF_16(len);
    wbuf[5] = BYTE_OFF_8(len);
    wbuf[6] = BYTE_OFF_0(len);
    ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, wbuf, FTS_CMD_ECC_CAL_LEN, 0);
    if (ret < 0) {
        printf("ecc calc cmd write fail");
        return ret;
    }

    uos_sleep(len / 256 /5);

    /* read status */
    for (i = 0; i < FTS_RETRIES_ECC_CAL; i++) {
        ret = i2c_read_regs(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, FTS_CMD_FLASH_STATUS, val,2, 0);
        read_status = (((uint16_t)val[0]) << 8) + val[1];
        /*  cplog_printf("%x %x", FTS_CMD_FLASH_STATUS_ECC_OK, read_status); */
        if (FTS_CMD_FLASH_STATUS_ECC_OK == read_status) {
            break;
        }
        uos_sleep(FTS_RETRIES_DELAY_ECC_CAL/5);
    }

    if (i >= FTS_RETRIES_ECC_CAL) {
        printf("ecc flash status read fail");
        return -1;
    }

    /* read out check sum */
    ret = i2c_read_regs(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, FTS_CMD_ECC_READ, val, 2,0);
    if (ret < 0) {
        printf( "ecc read cmd write fail");
        return ret;
    }

    ecc = (int)((uint16_t)(val[0] << 8) + val[1]);
    return ecc;
}

static int fts_fwupg_reset_to_boot(struct fts_upgrade *upg)
{
    int ret = 0;
    unsigned char reg[2] = {0};

    reg[0] = FTS_REG_UPGRADE;
    reg[1] = FTS_UPGRADE_AA;
    ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, reg, 2, 0);

    if (ret < 0) {
        printf("write FC=0xAA fail\n");

        return ret;
    }
    uos_sleep(FTS_DELAY_UPGRADE_AA);

    reg[0] = FTS_REG_UPGRADE;
    reg[1] = FTS_UPGRADE_55;
    ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, reg, 2, 0);

    if (ret < 0) {
        printf("write FC=0x55 fail\n");

        return ret;
    }

    uos_sleep(FTS_DELAY_UPGRADE_RESET/5);
    return 0;
}

static int fts_upgrage_check_id(unsigned char id_h, unsigned char id_l)
{
    int i = 0;
    unsigned char cmd = FTS_CMD_READ_ID;
    unsigned char buf[2] = { 0 };
    unsigned char reg[2] = {0};

    cmd = 0xC0;
    reg[0] = FTS_REG_UPGRADE;
    reg[1] = FTS_UPGRADE_AA;
    i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, reg, 2, 0);
    i2c_read_regs(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, cmd, buf ,2, 0);

    printf("cmd:%x %x\n", buf[0], buf[1]);
    reg[0] = FTS_REG_UPGRADE;
    reg[1] = FTS_UPGRADE_55;
    i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, reg, 2, 0);
    i2c_read_regs(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, cmd, buf ,2, 0);
    printf("cmd:%x %x", buf[0], buf[1]);
    uos_sleep(16);
    /*confirm in boot*/
    for (i = 0; i < 20; i++) {
        reg[0] = FTS_UPGRADE_55;
        reg[1] = FTS_UPGRADE_AA;
        i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, reg, 2, 0);
        uos_sleep(FTS_DELAY_UPGRADE_AA/5);
        cmd = FTS_CMD_READ_ID;
        i2c_read_regs(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, cmd, buf ,2, 0);

        if ((buf[0] != id_h) || (buf[1] != id_l)) {
            printf("check id fail,read id:0x%02x%02x != 0x%02x%02x,retry:%d\n",
                      buf[0], buf[1], id_h, id_l, i);
        } else{
            break;
        }
        reg[0] = FTS_REG_UPGRADE;
        reg[1] = FTS_UPGRADE_AA;
        i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, reg, 2, 0);
        reg[0] = FTS_REG_UPGRADE;
        reg[1] = FTS_UPGRADE_55;
        i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, reg, 2, 0);
        uos_sleep(FTS_DELAY_READ_ID/5);
    }

    if (i >= 10)
        return -1;

    printf("read boot id:%x %x\n", buf[0], buf[1]);

    return 0;
}

int fts_fwupg_enter_into_boot(void)
{
    int ret = 0;
    struct fts_upgrade *upg = &fwupgrade;

    printf("***********enter into pramboot/bootloader***********\n");
    ret = fts_fwupg_reset_to_boot(upg);

    if (ret < 0) {
        printf("enter into romboot/bootloader fail\n");

        return ret;
    }

    /*confirm TP controller is int romboot state*/
    ret = fts_upgrage_check_id(FTS_CHIP_IDH, FTS_CHIP_IDL);

    if (ret < 0) {
        printf("checking id fails");

        return -1;
    }

    return 0;
}

static int fts_ft6x36_upgrade(unsigned char *buf, unsigned int len)
{
    int ret = 0;
    unsigned int start_addr = 0;
    unsigned char cmd[4] = { 0 };
    unsigned int delay = 0;
    int ecc_in_host = 0;
    int ecc_in_tp = 0;

    if ((NULL == buf) || (len < FTS_MIN_LEN)) {
        printf("buffer/len(%x) is invalid", len);
        return -1;
    }

    /* enter into upgrade environment */
    ret = fts_fwupg_enter_into_boot();

    if (ret < 0) {
        printf("enter into pramboot/bootloader fail,ret=%d\n", ret);
        goto fw_reset;
    }

    cmd[0] = FTS_CMD_APP_DATA_LEN_INCELL;
    cmd[1] = BYTE_OFF_16(len);
    cmd[2] = BYTE_OFF_8(len);
    cmd[3] = BYTE_OFF_0(len);
    ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, cmd, FTS_CMD_DATA_LEN_LEN, 0);

    if (ret < 0) {
        printf("data len cmd write fail\n");
        goto fw_reset;
    }

    cmd[0] = FTS_CMD_FLASH_MODE;
    cmd[1] = FLASH_MODE_UPGRADE_VALUE;
    ret = i2c_write_bytes(I2C_MASTER_CI2C1, FT6X36_I2C_ADDR, cmd, 2, 0);

    if (ret < 0) {
        printf("upgrade mode(09) cmd write fail\n");
        goto fw_reset;
    }

    delay = FTS_DELAY_ERASE_PAGE_2K * (len / FTS_SIZE_PAGE_2K);
    ret = fts_fwupg_erase(delay);

    if (ret < 0) {
        printf("erase cmd write fail\n");
        goto fw_reset;
    }

    /* write app */
    start_addr = upgrade_func_ft6x36.appoff;
    ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1);

    if (ecc_in_host < 0 ) {
        printf("flash write fail\n");
        goto fw_reset;
    }

    /* ecc */
    ecc_in_tp = ft6x36_ecc_cal(start_addr, len);

    if (ecc_in_tp < 0 ) {
        printf("ecc read fail\n");
        goto fw_reset;
    }

    printf("ecc in tp:%x, host:%x\n", ecc_in_tp, ecc_in_host);

    if (ecc_in_tp != ecc_in_host) {
        printf("ecc check fail");
        goto fw_reset;
    }

    printf("upgrade success, reset to normal boot\n");
    ret = fts_fwupg_reset_in_boot();

    if (ret < 0) {
        printf("reset to normal boot fail\n");
    }

    uos_sleep(200/5);
    return 0;

fw_reset:
    printf("upgrade fail, reset to normal boot\n");
    ret = fts_fwupg_reset_in_boot();

    if (ret < 0) {
        printf("reset to normal boot fail\n");
    }
    return -1;
}

struct upgrade_func upgrade_func_ft6x36 = {
    .ctype = {0x8B},
    .fwveroff = 0x010E,
    .fwcfgoff = 0x0FB0,
    .appoff = 0x0000,
    .fw_ecc_check_mode = ECC_CHECK_MODE_CRC16,
    .upgrade = fts_ft6x36_upgrade,
};
#endif
